﻿Imports Org.BouncyCastle.Crypto.Parameters
Imports System.Security.Cryptography.Xml
Imports System.Xml
Imports Org.BouncyCastle.Security
Imports System.Security.Cryptography

Public Class XmlTool

    Public Shared Function SignXml(key As RSA, xmlToSign As String) As String
        'Create document
        Dim doc As New XmlDocument()
        doc.LoadXml(xmlToSign)

        'Document to sign
        Dim signedXML As New SignedXml(doc)

        'Convert BouncyCastle RSA key to .NET RSA key
        signedXML.SigningKey = key

        'Selects the nodes to sign in the xml
        Dim reference As New Reference()
        reference.Uri = ""

        'This transform assures us that the CA will read the XML the same exact way we wrote it
        Dim env As New XmlDsigEnvelopedSignatureTransform()
        reference.AddTransform(env)

        'Set the reference and compute signature
        signedXML.AddReference(reference)
        signedXML.ComputeSignature()

        'Retrieve signature and add it to the document
        Dim xmlDigitalSignature As XmlElement = signedXML.GetXml()
        doc.DocumentElement.AppendChild(doc.ImportNode(xmlDigitalSignature, True))

        Return doc.OuterXml
    End Function

    Public Shared Function SignXml(key As RsaPrivateCrtKeyParameters, xmlToSign As String) As String
        Dim dotNetRSAPrivateKey = DotNetUtilities.ToRSA(key)

        Return SignXml(dotNetRSAPrivateKey, xmlToSign)
    End Function

    Public Shared Function GenerateXML(root As String, elements As IEnumerable(Of KeyValuePair(Of String, String)), Optional id As Integer = -1) As String
        'Create document
        Dim doc As New XmlDocument()
        Using w As XmlWriter = doc.CreateNavigator().AppendChild()
            w.WriteStartDocument(True)

            w.WriteStartElement(root)

            If id > -1 Then
                w.WriteStartElement("ID")
                w.WriteString(id.ToString)
                w.WriteEndElement()
            End If

            For Each item In elements
                w.WriteStartElement(item.Key)
                w.WriteString(item.Value)
                w.WriteEndElement()
            Next

            w.WriteEndElement()

            w.WriteEndDocument()
            w.Close()
        End Using

        Return doc.OuterXml
    End Function

    Public Shared Function AddAttribute(xml As String, element As String, attributeName As String, attributeValue As String) As String
        'Create document
        Dim doc As New XmlDocument()
        doc.LoadXml(xml)

        Dim node = doc.GetElementsByTagName(element)(0)
        Dim att = doc.CreateAttribute(attributeName)
        att.InnerText = attributeValue

        node.Attributes.Append(att)


        Return doc.OuterXml
    End Function

    Public Shared Function AddElement(xml As String, parentElement As String, elementName As String, Optional elementInnerText As String = "") As String
        'Create document
        Dim doc As New XmlDocument()
        doc.LoadXml(xml)

        Dim node = doc.GetElementsByTagName(parentElement)(0)
        Dim newnode = doc.CreateElement(elementName)
        newnode.InnerText = elementInnerText
        node.AppendChild(newnode)

        Return doc.OuterXml
    End Function

    Public Shared Function GenerateXML(root As String, Optional id As Integer = -1) As String
        Dim x As New List(Of KeyValuePair(Of String, String))
        Return GenerateXML(root, x, id)
    End Function

    Public Shared Function GenerateXML(root As String, element As String, value As String, Optional id As Integer = -1) As String
        Dim x As New List(Of KeyValuePair(Of String, String))
        x.Add(New KeyValuePair(Of String, String)(element, value))
        Return GenerateXML(root, x, id)
    End Function

    Public Shared Function GetValue(elementName As String, xml As String) As String
        Dim doc As New XmlDocument
        doc.LoadXml(xml)
        Dim node = doc.GetElementsByTagName(elementName)(0)
        Return node.InnerText
    End Function

    Public Shared Function GetAttributeValue(elementName As String, attribute As String, xml As String) As String
        Dim doc As New XmlDocument
        doc.LoadXml(xml)
        Dim node = doc.GetElementsByTagName(elementName)(0).Attributes(attribute)
        Return node.Value
    End Function


    Public Shared Function CheckSignature(r As String, pubKey As RsaKeyParameters) As Boolean
        Dim x As New XmlDocument
        x.LoadXml(r)

        'Dim username = x.GetElementsByTagName("Username")(0).InnerText
        'Dim pubKeyBytes = x.GetElementsByTagName("PublicKey")(0).InnerText

        'Dim pubBCKey = DirectCast(Org.BouncyCastle.Security.PublicKeyFactory.CreateKey(Convert.FromBase64String(pubKeyBytes)), RsaKeyParameters)
        Dim pubRSAKey = DotNetUtilities.ToRSA(pubKey)


        Dim signedXml As New Xml.SignedXml(x)
        ' Find the "Signature" node and create a new
        ' XmlNodeList object.
        Dim nodeList As XmlNodeList = x.GetElementsByTagName("Signature")
        ' Throw an exception if no signature was found.
        If nodeList.Count <= 0 Then
            Return False
        End If

        ' Load the first <signature> node.  
        signedXml.LoadXml(CType(nodeList(0), XmlElement))

        'Selects the nodes to sign in the xml
        Dim reference As New Xml.Reference()
        reference.Uri = ""

        'This transform assures us that the CA will read the XML the same exact way we wrote it
        Dim env As New XmlDsigEnvelopedSignatureTransform()
        reference.AddTransform(env)

        'Set the reference and compute signature
        'signedXml.AddReference(reference)
        Dim signatureOk = signedXml.CheckSignature(pubRSAKey)

        Return signatureOk
    End Function

    Public Shared Function IsElementPresent(elementName As String, xml As String) As Boolean
        Dim doc As New XmlDocument
        doc.LoadXml(xml)
        Dim node = doc.GetElementsByTagName(elementName)
        If node.Count = 0 Then
            Return False
        Else
            Return True
        End If
    End Function
End Class
